package examples.ld;


import java.util.List;

import org.jgap.Configuration;
import org.jgap.Gene;
import org.jgap.IChromosome;
import org.jgap.IUniversalRateCalculator;
import org.jgap.InvalidConfigurationException;
import org.jgap.Population;
import org.jgap.RandomGenerator;
import org.jgap.impl.MutationOperator;
import org.jgap.util.ICloneable;

public class SwapMutation extends MutationOperator {

    private Container[] contChrom;
    private int m_startOffset = 0;
    private boolean adaptive;
    private double mutateRate;
    private double bestFit;
    private double avgFit;

    public SwapMutation() throws InvalidConfigurationException {
        super();
    }

    public SwapMutation(final Configuration a_config,
            Container[] container, double a_desiredMutationRate,
            boolean adapt)
            throws InvalidConfigurationException {
        super(a_config);
        contChrom = container;
        mutateRate = a_desiredMutationRate;
        adaptive = adapt;
    }

    public SwapMutation(final Configuration a_config,
            final IUniversalRateCalculator a_mutationRateCalculator)
            throws InvalidConfigurationException {
        super(a_config, a_mutationRateCalculator);

    }

    public SwapMutation(final Configuration a_config,
            final int a_desiredMutationRate, Container[] container)
            throws InvalidConfigurationException {
        super(a_config, a_desiredMutationRate);
        contChrom = container;
    }

    public void operate(final Population a_population,
            List a_candidateChromosomes) {
        // this was a private variable, now it is local reference.
        final IUniversalRateCalculator m_mutationRateCalc = getMutationRateCalc();
        // If the mutation rate is set to zero and dynamic mutation rate is
        // disabled, then we don't perform any mutation.
        // ----------------------------------------------------------------
        if (getMutationRate() == 0 && m_mutationRateCalc == null) {
            return;
        }
        // Determine the mutation rate. If dynamic rate is enabled, then
        // calculate it based upon the number of genes in the chromosome.
        // Otherwise, go with the mutation rate set upon construction.
        // --------------------------------------------------------------
        int currentRate;
        if (m_mutationRateCalc != null) {
            currentRate = m_mutationRateCalc.calculateCurrentRate();
        } else {
            currentRate = getMutationRate();
        }

        RandomGenerator generator = getConfiguration().getRandomGenerator();
        // It would be inefficient to create copies of each Chromosome just
        // to decide whether to mutate them. Instead, we only make a copy
        // once we've positively decided to perform a mutation.
        // ----------------------------------------------------------------
        int size = a_population.size();
        for (int i = 0; i < size; i++) {
            IChromosome x = a_population.getChromosome(i);
            // This returns null if not mutated:
            IChromosome xm = operate(x, currentRate, generator);
            if (xm != null) {
                a_candidateChromosomes.add(xm);
            }
        }
    }

    protected IChromosome operate(final IChromosome a_x, final int a_rate,
            final RandomGenerator a_generator) {
        IChromosome chromosome = null;
        // ----------------------------------------
        for (int j = m_startOffset; j < a_x.size(); j++) {
            double rate = mutateRate;
            if (chromosome == null) {
                chromosome = (IChromosome) ((ICloneable) a_x).clone();
            }
            if (adaptive) {
                double fitVal = chromosome.getFitnessValue();
                rate = rate * bestFit / fitVal;
                if (fitVal < avgFit) {
                    if (rate < 0.5) {
                        rate = 0.5;
                    }
                }
                if (rate > 1.0) {
                    rate = 1.0;
                }
            }
            if (a_generator.nextInt(10000) < (rate * 10000)) {
                Gene[] genes = chromosome.getGenes();
                Gene[] mutated = operate(a_generator, j, genes);
                // this.setMutationRate(this.getMutationRate()-1);

                // setGenes is not required for this operator, but it may
                // be needed for the derived operators.
                // ------------------------------------------------------
                try {
                    chromosome.setGenes(mutated);
                } catch (InvalidConfigurationException cex) {
                    throw new Error(
                            "Gene type not allowed by constraint checker", cex);
                }
            }
        }
        return chromosome;
    }

    protected Gene[] operate(final RandomGenerator a_generator,
            final int a_target_gene, final Gene[] a_genes) {
        // swap this gene with the other one now:
        // mutateGene(genes[j], generator);
        // -------------------------------------
        int other = m_startOffset
                + a_generator.nextInt(a_genes.length - m_startOffset);

        int id = (Integer) a_genes[a_target_gene].getAllele();
        int id1 = (Integer) a_genes[other].getAllele();

        int offset = 0;
        int offset1 = 0;
        // start type ==20, swap type == 20;
        if ((contChrom[id].getType() == 20) && (contChrom[id1].getType() == 20)) {
            Gene t = a_genes[a_target_gene];
            a_genes[a_target_gene] = a_genes[other];
            a_genes[other] = t;

        } // start type == 20,swap type ==40
        else // if((contChrom[id].getType()==20)&&(contChrom[id1].getType()==40))
        {
            if (a_target_gene % 2 == 1) {
                offset = -1;
            }
            if (other % 2 == 1) {
                offset1 = -1;
            }

            Gene t = a_genes[a_target_gene + offset];
            a_genes[a_target_gene + offset] = a_genes[other + offset1];
            a_genes[other + offset1] = t;

            Gene t1 = a_genes[a_target_gene + offset + 1];
            a_genes[a_target_gene + offset + 1] = a_genes[other + offset1 + 1];
            a_genes[other + offset1 + 1] = t1;
        }
        /*
         * //start type == 20,swap type ==40
         * if((contChrom[id].getType()==20)&&(contChrom[id1].getType()==40)) {
         * if(a_target_gene%2==1) { offset = -1; }
         * 
         * Gene t = a_genes[a_target_gene]; a_genes[a_target_gene] =
         * a_genes[other]; a_genes[other] = t;
         * 
         * Gene t1 = a_genes[a_target_gene+offset];
         * a_genes[a_target_gene+offset] = a_genes[other+offset];
         * a_genes[other+offset] = t1; }
         * 
         * //start type == 40,swap type ==40
         * if((contChrom[id].getType()==40)&&(contChrom[id1].getType()==40)) {
         * if(a_target_gene%2==1) { offset = -1; }
         * 
         * Gene t = a_genes[a_target_gene]; a_genes[a_target_gene] =
         * a_genes[other]; a_genes[other] = t;
         * 
         * Gene t1 = a_genes[a_target_gene+offset];
         * a_genes[a_target_gene+offset] = a_genes[other+offset];
         * a_genes[other+offset] = t1;
         * 
         * } //start type == 40,swap type ==20
         * if((contChrom[id].getType()==40)&&(contChrom[id1].getType()==20)) {
         * Gene t = a_genes[a_target_gene]; a_genes[a_target_gene] =
         * a_genes[other]; a_genes[other] = t;
         * 
         * }
         */

        return a_genes;
    }

    public void setStartOffset(final int a_offset) {
        m_startOffset = a_offset;
    }

    public int getStartOffset() {
        return m_startOffset;
    }

    public double getRate() {
        return mutateRate;
    }

    public void setRate(double rate) {
        mutateRate = rate;
    }

    public void setBestRate(double rate, double avg) {
        bestFit = rate;
        avgFit = avg;
    }
}
